package edu.tfh.s2.ehkbot;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.InvalidPropertiesFormatException;

import javax.swing.JFrame;

import edu.tfh.s2.ehkbot.daten.Einstellungen;
import edu.tfh.s2.ehkbot.daten.InitFactory;
import edu.tfh.s2.ehkbot.daten.PrototypFactory;
import edu.tfh.s2.ehkbot.daten.InitFactory.InitFactoryState;
import edu.tfh.s2.ehkbot.daten.Protokoll;
import edu.tfh.s2.ehkbot.daten.Zeitgeber;
import edu.tfh.s2.ehkbot.daten.simulation.InitFactorySimulation;
import edu.tfh.s2.ehkbot.daten.simulation.ZeitgeberSimulation;
import edu.tfh.s2.ehkbot.gerete.EHKBot;
import edu.tfh.s2.ehkbot.gerete.EHKBot.EHKBotInitFail;
import edu.tfh.s2.ehkbot.gerete.sensoren.BarcodeScanner;
import edu.tfh.s2.ehkbot.gerete.sensoren.Kamera;
import edu.tfh.s2.ehkbot.gerete.sensoren.simulation.BarcodeScannerSimulation;
import edu.tfh.s2.ehkbot.gerete.sensoren.simulation.IndoorGPSSimulation;
import edu.tfh.s2.ehkbot.gerete.sensoren.simulation.KameraSimulation;
import edu.tfh.s2.ehkbot.gerete.sensoren.simulation.SensorStorryFahre;
import edu.tfh.s2.ehkbot.gerete.sensoren.simulation.SensorStorryGreife;
import edu.tfh.s2.ehkbot.inventar.Einkaufsauftrag;
import edu.tfh.s2.ehkbot.inventar.Geschaeft;
import edu.tfh.s2.ehkbot.inventar.Warenbox;
import edu.tfh.s2.ehkbot.inventar.WarenboxRegal.KeineLeereBox;
import edu.tfh.s2.ehkbot.simulation.SimulationGUI;
import edu.tfh.s2.ehkbot.simulation.TerminalGUI;
import edu.tfh.s2.ehkbot.steuerung.EinkaufsAuftragAusfuehren;
import edu.tfh.s2.ehkbot.steuerung.Karte;
import edu.tfh.s2.ehkbot.steuerung.Position;
import edu.tfh.s2.ehkbot.steuerung.Richtung;
import edu.tfh.s2.ehkbot.steuerung.schnittstellen.AuftragsQuelle;
import edu.tfh.s2.ehkbot.steuerung.schnittstellen.Netzwerk.NetzwerkState;
import edu.tfh.s2.ehkbot.steuerung.schnittstellen.simulation.NetzwerkStateSimulation;

/**
 * @author s2zehn Die Mutter Klasse die alles Startet
 */
public class EHKBotMain {
	/**
	 * Wird geworfen wenn Simulation aus irgend einen Grund nicht möglich ist-
	 */
	public static class SimulationNichtMoeglich extends Exception {
		private static final long serialVersionUID = 9170177025543968313L;

		public SimulationNichtMoeglich() {
			super();
		}

		public SimulationNichtMoeglich(String arg0) {
			super(arg0);
		}

		public SimulationNichtMoeglich(String arg0, Throwable arg1) {
			super(arg0, arg1);
		}

		public SimulationNichtMoeglich(Throwable arg0) {
			super(arg0);
		}
	}

	static private EHKBotMain instance = null;

	/**
	 * Aktiviert die SimulationGUI, wenn man eine Simulation machen will, muss man die Methode aufrufen bevor
	 * eine Instance von EHKBotMain erzeugt wird.
	 * 
	 * @throws SimulationNichtMoeglich
	 *             Wenn enableSimulation() aufgerufen wurde nachdem EHKBotMain.getInstance() aufgerufen wurde.
	 */
	public static void enableSimulation() throws SimulationNichtMoeglich {
		if (instance != null)
			throw new SimulationNichtMoeglich();

		/**
		 * Lade aus den argumenten die einstellungen. Laden aus der GUI ist hier nicht angebracht da die GUI
		 * noch nicht exestiert und sie selber auf werte aus Einstellungen zurückgreifen könnte
		 */
		try {
			Einstellungen.getInstance().loadFromXML(new FileInputStream(new File("einstellungen.properties")));
		} catch (InvalidPropertiesFormatException e) {
			Protokoll.getInstanc().exception(e);
			throw new SimulationNichtMoeglich();
		} catch (FileNotFoundException e) {
			Protokoll.getInstanc().exception(e);
			throw new SimulationNichtMoeglich();
		} catch (IOException e) {
			Protokoll.getInstanc().exception(e);
			throw new SimulationNichtMoeglich();
		}

		/**
		 * Aktivert die Zeitgeber SimulationGUI, mit der geschwindigkeit: echtzeit *
		 * Einstellungen.getInstance().get("Zeitraffer") Zeitgeber SimulationGUI ist wichtig für
		 * MotorAktorSimulation
		 */
		double zeitraffer = Double.parseDouble(Einstellungen.getInstance().getProperty("Zeitraffer"));
		ZeitgeberSimulation zeitSimu = new ZeitgeberSimulation(zeitraffer);

		IndoorGPSSimulation indoorgps = null;

		int x = Integer.parseInt(Einstellungen.getInstance().getProperty("StartX"));
		int y = Integer.parseInt(Einstellungen.getInstance().getProperty("StartY"));
		int z = Integer.parseInt(Einstellungen.getInstance().getProperty("StartZ"));
		double richtung = Double.parseDouble(Einstellungen.getInstance().getProperty("StartRichtung"));
		Position startPosition = new Position(x, y, z);
		indoorgps = new IndoorGPSSimulation(startPosition, new Richtung(richtung));

		Kamera kamerSim = new Kamera(new KameraSimulation());
		BarcodeScanner barcodeScannerSim = new BarcodeScanner(new BarcodeScannerSimulation());
		NetzwerkState netzwerkSim = new NetzwerkStateSimulation();

		/**
		 * Die Factory liefert je nachdem ob es sich um eine Simulation handelt unterschiedliche Objekte,
		 * damit kann ein großen teil des Codes unabhänig von der Simulation geschreiben werden
		 */
		PrototypFactory prototypFactory = new PrototypFactory(
			zeitSimu,
			barcodeScannerSim,
			kamerSim,
			netzwerkSim);
		InitFactoryState initFactoryState = new InitFactorySimulation(
			new File("KartenBild.txt"),
			prototypFactory,
			indoorgps);
		InitFactory.getInstance().setState(initFactoryState);

		try {
			SimulationGUI simGUI = SimulationGUI.getInstance();
			simGUI.loadTablesFromFile(simGUI.getWarenBestandTable(), new File("waren.warenbestand"));
			simGUI.loadTablesFromFile(simGUI.getWarenboxRegalTabel(), new File("warenboxf.warenboxfaecher"));
			simGUI.loadTablesFromFile(simGUI.getAuftraegeTable(), new File("auf01.auftrag"));
		} catch (IOException e) {
			Protokoll.getInstanc().exception(e);
			throw new SimulationNichtMoeglich();
		} catch (ClassNotFoundException e) {
			Protokoll.getInstanc().exception(e);
			throw new SimulationNichtMoeglich();
		}

		/**
		 * IndoorGPSSimulation ist beobachter von Fahrgestell
		 */
		indoorgps.registerSubject(EHKBotMain.getInstance().ehkbot.getFahrgestell());
	}

	/**
	 * Siehe Singelton Pattern
	 * 
	 * @return Die einige Instanc von EHKBotMain
	 */
	static public EHKBotMain getInstance() {
		if (instance == null)
			instance = new EHKBotMain();
		return instance;
	}

	/**
	 * @return Liefer true wenn SimulationGUI aktivert wurde.
	 */
	public static boolean isSimulation() {
		return SimulationGUI.isOn();
	}

	public static void main(String[] arg) throws InterruptedException, SimulationNichtMoeglich {
		/**
		 * In real betrieb würde das if prüfen ob das argument --simulation vorhanden ist
		 */
		// if(arg.length > 1 && arg[1].equals("--simulation"))
		{
			enableSimulation();
			showSimalation();

		}
		SensorStorryFahre ssf = new SensorStorryFahre(100);
		ssf.start();
		while (true) {
			EHKBotMain.getInstance().starteAuftrag();
			Zeitgeber.getInstance().sleep(1000);
		}
	}

	/**
	 * Zeigt die Simulations Gui
	 */
	private static void showSimalation() {
		SimulationGUI.getInstance().setVisible(true);
		SimulationGUI.getInstance().setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}

	private AuftragsQuelle auftragsQuelle;

	private EHKBot ehkbot;

	private Geschaeft geschaeft;

	private Karte karte;

	private EHKBotMain() {
		try {
			ehkbot = new EHKBot();
		} catch (EHKBotInitFail e) {
			Protokoll.getInstanc().exception(e);
		}
		geschaeft = new Geschaeft(
			InitFactory.getInstance().getWarenbestand(),
			InitFactory.getInstance().getWarenboxregal());
		auftragsQuelle = new AuftragsQuelle();
		karte = InitFactory.getInstance().getKarte();
	}

	/**
	 * @return Liefert den EHKBot
	 */
	public EHKBot getEHKBot() {
		return ehkbot;
	}

	/**
	 * @return Liefert das Geschaeft
	 */
	public Geschaeft getGeschaeft() {
		return geschaeft;
	}

	/**
	 * @return Liefert die für das System gültige Karte
	 */
	public Karte getKarte() {
		return karte;
	}

	/**
	 * Die Hauptarbeits Methode versucht einen Auftrag zu empfangen und übergibt ihn an die Ausfäuhrenden
	 * Klassen/Objekte
	 */
	public void starteAuftrag() {
		Einkaufsauftrag auftrag = null;
		try {
			auftrag = auftragsQuelle.getAuftrag();
		} catch (IOException e) {
			Protokoll.getInstanc().exception(e);
		} catch (ClassNotFoundException e) {
			Protokoll.getInstanc().exception(e);
		}
		if (auftrag != null) {
			Protokoll.getInstanc().protokolliere("EHKBotMain", "empfange auftrag", 3, Protokoll.TYP.INFO);

			try {
				Warenbox w = geschaeft.getWarenboxregal().sucheLeereWarenbox();
				auftrag.setWarenbox(w);

				String msg = String.format(
					"Warenbox (bc%d) wurde an Einkaufsauftrag (%d) zugewiesen",
					w.getBarcode(),
					auftrag.getAuftragID());
				Protokoll.getInstanc().protokolliere("EHKBotMain", msg, 2, Protokoll.TYP.INFO);
			} catch (KeineLeereBox e) {
				Protokoll.getInstanc().exception(e);
				return;
			}

			if (isSimulation())
				TerminalGUI.getInstance().refresh(auftrag);
			EinkaufsAuftragAusfuehren ausfuehrer = new EinkaufsAuftragAusfuehren();
			ausfuehrer.fuehreEinkaufsAuftragAus(auftrag);
		}
	}
}
